#include "Instructor.h"
//#include "ReceptorChat.h"

using std::endl;
using std::cerr;
using std::string;

Instructor::Instructor(char* ip_serv, char* puerto, char* puerto_av)
									:Usuario(ip_serv, puerto, puerto_av) {
	en_clase = false;
	en_pausa = false;
}

int Instructor::crearCurso(std::string codCurso, std::string Descripcion, std::string Periodo) {
    char cod_op = 'D', codRespuesta;
    int tam_arg = codCurso.size() + Descripcion.size() + 2;
    std::string aux;
    unsigned int codigo = 0;
    codigo = this->String_toInt(codCurso);

    // Codigo vacio
    if((codCurso.size() <= 0) || (Descripcion.size() <= 0)) return -3;

    // Codigo invalido
    if(codigo <= 0) return -2;

    aux = this->Int_toString(codigo);

    // Ingrese numeros
    if(aux.size() != codCurso.size()) return -4;

    if(cursos.find(codigo) != cursos.end()) return -5;

    if(!socket->enviar(&cod_op, sizeof(cod_op))) {
    	std::cerr << "no se pudo enviar" << std::endl;
    	return -1;
    }
    if(!socket->enviar(&tam_arg, sizeof(tam_arg))) {
    	std::cerr << "no se pudo enviar" << std::endl;
    	return -1;
    }
    if(!socket->enviar(codCurso.c_str(), codCurso.size() + 1)) {
    	std::cerr << "no se pudo enviar" << std::endl;
    	return -1;
    }
    if(!socket->enviar(Descripcion.c_str(), Descripcion.size() + 1)) {
    	std::cerr << "no se pudo enviar" << std::endl;
    	return -1;
    }
    if(!this->recibirRespuesta(&codRespuesta) == 0) {
    	std::cerr << "No se pudo recibir la respuesta de crear clase" << std::endl;
    	return -1;
    }
    if(codRespuesta == '1') {
        cursos[codigo];
        cursosFinalizados[codigo];
    	return 0;
    }
    if(codRespuesta == '0') {
    	return -6;
    }
    return 0;

}

int Instructor::anadirClase(std::string codCurso, std::string codClase,
		std::string descripcion, std::string clave, bool disponible) {
    char cod_op = 'E', codRespuesta;
    int tam_arg = codCurso.size() + codClase.size() + descripcion.size() + 3 + sizeof(char);
    std::string aux1, aux2;
    unsigned int cod_Curso = 0;
    unsigned int cod_Clase = 0;
    char codDisponibilidad = 'N';
    if(disponible)
    	codDisponibilidad = 'S';
    std::list<unsigned int>::iterator it;
    // Pasaje de string a entero sin signo
    cod_Curso = this->String_toInt(codCurso);
    cod_Clase = this->String_toInt(codClase);

    if((codCurso.size() <= 0) || (codClase.size() <= 0) || (descripcion.size() <= 0)) {
    	//std::cerr << "Los códigos son campos necesarios" << std::endl;
    	return -3;
    }
    if((cod_Curso <= 0) || (cod_Clase <= 0)) {
    	//std::cerr << "Codigo invalido, por favor ingrese solo letras" << std::endl;
    	return -2;
    }
    aux1 = this->Int_toString(cod_Curso);
    aux2 = this->Int_toString(cod_Clase);
    if((aux1.size() != codCurso.size()) || (aux2.size() != codClase.size())) {
    	//std::cerr << "En el codigo ingrese solo numeros y sin ceros adelante" << std::endl;
    	return -4;
    }

    // Pregunta si existe el curso
    if(!this->buscarCurso(cod_Curso))
            return -5;

    // Curso ya empezado no se pueden añadir mas clases
    if(cursosFinalizados[cod_Curso].size() > 0) {
    	//std::cerr << "Curso ya empezado" << std::endl;
    	return -7;
    }

    // Pregunta si ya existe la clase a anadir
    if(this->buscarClaseDeCurso(cod_Curso, cod_Clase))
    	return -6;

    if(!socket->enviar(&cod_op, sizeof(cod_op))) {
            std::cerr << "no se pudo enviar" << std::endl;
            return -1;
    }
    if(!socket->enviar(&tam_arg, sizeof(tam_arg))) {
            std::cerr << "no se pudo enviar" << std::endl;
            return -1;
    }
    if(!socket->enviar(codCurso.c_str(), codCurso.size() + 1)) {
            std::cerr << "no se pudo enviar" << std::endl;
            return -1;
    }
    if(!socket->enviar(codClase.c_str(), codClase.size() + 1)) {
            std::cerr << "no se pudo enviar" << std::endl;
            return -1;
    }
    if(!socket->enviar(descripcion.c_str(), descripcion.size() + 1)) {
            std::cerr << "no se pudo enviar" << std::endl;
            return -1;
    }
    if(!socket->enviar(&codDisponibilidad, sizeof(codDisponibilidad))) {
            std::cerr << "No se pudo enviar" << std::endl;
            return -1;
    }
    if(!this->recibirRespuesta(&codRespuesta) == 0) {
    	std::cerr << "No se pudo recibir la respuesta de crear clase" << std::endl;
    	return -1;
    }
    if(codRespuesta == '2') {
    	return -6;
    };
    if(codRespuesta == '1') {
    	cursos[cod_Curso].push_back(cod_Clase);
    	return 0;
    }
    if(codRespuesta == '0') {
    	return -5;
    };
    return 0;
}

int Instructor::crearClase(std::string codClase, std::string descripcion,
		std::string clave, bool disponible) {
    char cod_op = 'C', codRespuesta;
    int tam_arg = codClase.size() + descripcion.size() + 2 + sizeof(char);
    unsigned int cod_Clase = 0;
    cod_Clase = this->String_toInt(codClase);
    std::string aux1;
    char codDisponibilidad = 'N';
    if(disponible)
    	codDisponibilidad = 'S';

    if((codClase.size() <= 0) || (descripcion.size() <= 0)) {
    	std::cerr << "El codigo es un campo necesario" << std::endl;
    	return -3;
    }

    if(cod_Clase <= 0) {
    	std::cerr << "Codigo invalido, por favor ingrese solo letras" << std::endl;
    	return -4;
    }

    aux1 = this->Int_toString(cod_Clase);

    if(aux1.size() != codClase.size()) {
    	std::cerr << "En el codigo ingrese solo numeros y sin ceros adelante" << std::endl;
    	return -5;
    }

    if(this->buscarClase(cod_Clase))
            return -6; // Clase creada por este instructor

    if(!socket->enviar(&cod_op, sizeof(cod_op))) {
            std::cerr << "No se pudo enviar" << std::endl;
            return -1;
    }
    if(!socket->enviar(&tam_arg, sizeof(tam_arg))) {
            std::cerr << "No se pudo enviar" << std::endl;
            return -1;
    }
    if(!socket->enviar(codClase.c_str(), codClase.size() + 1)) {
            std::cerr << "No se pudo enviar" << std::endl;
            return -1;
    }
    if(!socket->enviar(descripcion.c_str(), descripcion.size() + 1)) {
            std::cerr << "No se pudo enviar" << std::endl;
            return -1;
    }
    if(!socket->enviar(&codDisponibilidad, sizeof(codDisponibilidad))) {
            std::cerr << "No se pudo enviar" << std::endl;
            return -1;
    }
    if(!this->recibirRespuesta(&codRespuesta) == 0) {
    	std::cerr << "No se pudo recibir la respuesta de crear clase" << std::endl;
    	return -1;
    }
    if(codRespuesta == '1') {
    	clases.push_back(cod_Clase);
    	return 0;
    }
    if(codRespuesta == '0') {
    	return -2;
    }
    return 0;
}

int Instructor::iniciarClase(std::string codCurso, std::string codClase) {
    char cod_op = 'A';
    int tam_arg = 0;
    unsigned int cod_Clase = 0;
    unsigned int cod_Curso = 0;
    cod_Clase = this->String_toInt(codClase);
    bool curso = false;
    std::string aux1, aux2;

    if(codClase.size() <= 0) {
    	// Los códigos son campos necesarios
    	return -2;
    }
    if(cod_Clase <= 0) {
    	// Codigo invalido
    	return -3;
    }

    aux2 = this->Int_toString(cod_Clase);
    if(aux2.size() != codClase.size()) {
    	return -4;
    }

    if(codCurso.size() <= 0) {
    	// Pregunto si el creo la clase individual
    	if(!this->buscarClase(cod_Clase))
    		return -5;   // Clase no encontrada
    	if(this->buscarClaseTerminada(cod_Clase))
    		return -7;
    	tam_arg = codClase.size() + 1;
    }
    else {
    	cod_Curso = this->String_toInt(codCurso);
    	if(cod_Curso <= 0)
    		return -3;

    	aux1 = this->Int_toString(cod_Curso);
        if(aux1.size() != codCurso.size()) {
        	return -4;
        }

    	aux1 = this->Int_toString(cod_Curso);
    	// Pregunto si el creo el curso
    	if(!this->buscarCurso(cod_Curso))
    		return -5;
    	// Pregunto  si el creo la clase dentro del curso
    	if(!this->buscarClaseDeCurso(cod_Curso, cod_Clase))
    		return -6;
    	if(this->buscarClaseDeCursoTerminada(cod_Curso, cod_Clase))
    		return -7;
    	curso = true;
    	tam_arg = codClase.size() + codCurso.size() + 2;
    }

    if(!this->socket->enviar(&cod_op, sizeof(cod_op))) {
            std::cerr << "No se pudo iniciar la clase" << std::endl;
            return -1;
    }
    if(!this->socket->enviar(&tam_arg, sizeof(tam_arg))) {
            std::cerr << "No se pudo iniciar la clase" << std::endl;
            return -1;
    }
    if(!this->socket->enviar(codClase.c_str(), codClase.size() + 1)) {
            std::cerr << "No se pudo iniciar la clase" << std::endl;
            return -1;
    }
    if(curso) {
            if(!this->socket->enviar(codCurso.c_str(), codCurso.size() + 1)) {
                    std::cerr << "No se pudo iniciar la clase" << std::endl;
                    return -1;
            }
            curso_enAccion = codCurso;
    }
    clase_enAccion = codClase;
    en_clase = true;
    en_pausa = false;
    microfono = new Microfono();
    emisorDeVideo = new EmisorDeFrames(socket_av, &en_clase, microfono);
    // Corre el hilo de "captura de frames"
    emisorDeVideo->start();
    // Corre el hilo del receptor de chat
//    this->receptor_chat = new ReceptorChat(this, this->socket);
//    this->receptor_chat->start();
    return 0;
}

int Instructor::pausarClase() {
	char cod_op = 'B';
	int tam_arg = 0;
	if((!en_clase) || (en_pausa))
		return -2;   // No se inicio ninguna clase
	emisorDeVideo->pararTransmision();
	emisorDeVideo->join();
	if(!socket->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo terminar la clase" << std::endl;
		return -1;
	}
	if(!socket->enviar(&tam_arg, sizeof(tam_arg))) {
		std::cerr << "No se pudo terminar la clase" << std::endl;
		return -1;
	}
	cod_op = 'E';
	if(!socket_av->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo terminar la clase" << std::endl;
		return -1;
	}
	en_pausa = true;
	return 0;
}

int Instructor::continuarClase() {
	char cod_op = 'G';
	int tam_arg = 0;
	if((!en_clase) || (!en_pausa))
		return -3;   // Ya se encuentra en una clase

	if(!this->socket->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo iniciar la clase" << std::endl;
		return -1;
	}
	if(!this->socket->enviar(&tam_arg, sizeof(tam_arg))) {
		std::cerr << "No se pudo iniciar la clase" << std::endl;
		return -1;
	}
	en_pausa = false;
	// Corre el hilo de "captura de frames"
	emisorDeVideo->seguirTransmision();
	emisorDeVideo->start();
	return 0;
}

int Instructor::terminarClase() {
	char cod_op = 'H';
	int tam_arg = 0;
	if((!en_clase) || (en_pausa))
		return -2;   // No se inicio ninguna clase
	emisorDeVideo->pararTransmision();
	emisorDeVideo->join();
	delete emisorDeVideo;
	if(!socket->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo terminar la clase" << std::endl;
		return -1;
	}
	if(!socket->enviar(&tam_arg, sizeof(tam_arg))) {
		std::cerr << "No se pudo terminar la clase" << std::endl;
		return -1;
	}
	cod_op = 'Z';
	if(!socket_av->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo terminar la clase" << std::endl;
		return -1;
	}
	en_clase = false;
	if(curso_enAccion.size() == 0) {
		clasesFinalizadas.push_back(String_toInt(clase_enAccion));
	}
	if(curso_enAccion.size() > 0) {
		cursosFinalizados[String_toInt(curso_enAccion)].push_back(String_toInt(clase_enAccion));
	}
	clase_enAccion.clear();
	curso_enAccion.clear();
//	this->receptor_chat->terminar_recepcion();
//	this->receptor_chat->join();
//	delete this->receptor_chat;
//	this->receptor_chat = NULL;
	return 0;
}
void Instructor::crear_hilos_de_recepcion() {
}

// Devuelte true si ya esta inscripto en esa clase
bool Instructor::buscarClase(unsigned int cod_Clase) {
    std::list<unsigned int>::iterator it;
    for(it = clases.begin(); it != clases.end(); it++) {
            if((*it) == cod_Clase) {
                    std::cerr << "Clase ya existente/inscripta" << std::endl;
                    return true;
            }
    }
    std::cerr << "Clase inexistente/no inscripta" << std::endl;
    return false;
}

// Retorna true si el curso existe
bool Instructor::buscarCurso(unsigned int cod_Curso) {
    std::map<unsigned int, std::list<unsigned int> >::iterator it;
    for(it = cursos.begin(); it != cursos.end(); it++) {
            if((*it).first == cod_Curso) {
                    std::cerr << "Curso ya creado por usted" << std::endl;
                    return true;
            }
    }
    std::cerr << "Curso no creado por usted" << std::endl;
    return false;
}

// Retorna true si la clase existe o si ya esta inscripto
bool Instructor::buscarClaseDeCurso(unsigned int cod_Curso, unsigned int cod_Clase) {
    std::list<unsigned int>::iterator it;
    for(it = cursos[cod_Curso].begin(); it != cursos[cod_Curso].end(); ++it) {
            if((*it) == cod_Clase) {
                    std::cerr << "La clase existe dentro del curso" << std::endl;
                    return true;
            }
    }
    std::cerr << "La clase no pertence al curso" << std::endl;
    return false;
}

// Devuelte true si ya esta finalizada en esa clase
bool Instructor::buscarClaseTerminada(unsigned int cod_Clase) {
    std::list<unsigned int>::iterator it;
    for(it = clasesFinalizadas.begin(); it != clasesFinalizadas.end(); it++) {
            if((*it) == cod_Clase) {
                    std::cerr << "Clase ya finalizo, no puede ser iniciada" << std::endl;
                    return true;
            }
    }
    std::cerr << "Clase no finalizada" << std::endl;
    return false;
}

// Retorna true si la clase existe o si ya esta inscripto
bool Instructor::buscarClaseDeCursoTerminada(unsigned int cod_Curso, unsigned int cod_Clase) {
    std::list<unsigned int>::iterator it;
    for(it = cursosFinalizados[cod_Curso].begin();
    		it != cursosFinalizados[cod_Curso].end(); ++it) {
            if((*it) == cod_Clase) {
                    std::cerr << "La clase del curso ya finalizo" << std::endl;
                    return true;
            }
    }
    std::cerr << "Clase del curso no finalizada" << std::endl;
    return false;
}

bool Instructor::esta_en_clase() {
  return en_clase;
}

std::string Instructor::getClase_EnAccion() {
	return clase_enAccion;
}

int Instructor::adjuntar(std::string directorio, std::string codigoClase,
		std::string codigoCurso) {
	std::string nom_arch = "";
	if(codigoCurso == "0") {
		if(!buscarClase(String_toInt(codigoClase)))
			return 1;
		if(buscarClaseTerminada(String_toInt(codigoClase)))
			return 1;
	}
	else {
		if(!buscarClaseDeCurso(String_toInt(codigoCurso), String_toInt(codigoClase)))
			return 1;
		if(buscarClaseDeCursoTerminada(String_toInt(codigoCurso), String_toInt(codigoClase)))
			return 1;
	}
	if(codigoCurso != "0")
		nom_arch += codigoCurso + "_";
	nom_arch += codigoClase + "_";
	std::ifstream arch_adj;
	arch_adj.open(directorio.c_str(), std::ios_base::binary);
	nom_arch.append(directorio, directorio.find_last_of('/') + 1, directorio.size());
	arch_adj.seekg(0, std::ios::end);
	unsigned long tam_arch = arch_adj.tellg();
	int tam_arg = nom_arch.size() + 1 + sizeof(tam_arch) + codigoClase.size()
			+ 1 + codigoCurso.size() + 1;
	char cod_op = 'I';
	if(!socket->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "Error enviando archivo" << std::endl;
		return -1;
	}
	if(!socket->enviar(&tam_arg, sizeof(tam_arg))) {
		std::cerr << "Error enviando archivo" << std::endl;
		return -1;
	}
	if(!socket->enviar(nom_arch.c_str(), nom_arch.size() + 1)) {
		std::cerr << "Error enviando archivo" << std::endl;
		return -1;
	}
	if(!socket->enviar(&tam_arch, sizeof(tam_arch))) {
		std::cerr << "Error enviando archivo" << std::endl;
		return -1;
	}
	if(!socket->enviar(codigoClase.c_str(), codigoClase.size() + 1)) {
		std::cerr << "Error enviando archivo" << std::endl;
		return -1;
	}
	if(!socket->enviar(codigoCurso.c_str(), codigoCurso.size() + 1)) {
		std::cerr << "Error enviando archivo" << std::endl;
		return -1;
	}
	cod_op = 'J';
	const int tam_paquete = 1024;
	unsigned long tam_envio = tam_paquete;
	char paquete[tam_paquete];
	arch_adj.seekg(0, std::ios::beg);
	while(tam_arch > 0) {
		if(tam_arch < tam_envio)
			tam_envio = tam_arch;
		arch_adj.read(paquete, tam_envio);
		if(!socket->enviar(&cod_op, sizeof(cod_op))) {
			std::cerr << "Error enviando archivo" << std::endl;
			return -1;
		}
		if(!socket->enviar(&tam_envio, sizeof(tam_envio))) {
			std::cerr << "Error enviando archivo" << std::endl;
			return -1;
		}
		if(!socket->enviar(paquete, tam_envio)) {
			std::cerr << "Error enviando archivo" << std::endl;
			return -1;
		}
		tam_arch -= tam_envio;
	}
	return 0;
}

unsigned int Instructor::String_toInt(std::string str) {
	unsigned int entero = 0;
	std::stringstream ss;
	ss << str;
	ss >> entero;
	return entero;
}

Instructor::~Instructor() {
	if(en_clase) {
		emisorDeVideo->join();
		delete emisorDeVideo;
		delete microfono;
//		receptor_chat->terminar_recepcion();
//		receptor_chat->join();
//		delete receptor_chat;
	}
}
